package com.test.mytest.javacv;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.imageio.ImageIO;

import org.bytedeco.javacpp.opencv_core;
import org.bytedeco.javacpp.opencv_core.CvMemStorage;
import org.bytedeco.javacpp.opencv_core.CvRect;
import org.bytedeco.javacpp.opencv_core.CvSeq;
import org.bytedeco.javacpp.opencv_core.IplImage;
import org.bytedeco.javacpp.opencv_imgproc;
import org.bytedeco.javacpp.opencv_objdetect;
import org.bytedeco.javacpp.opencv_objdetect.CvHaarClassifierCascade;
import org.bytedeco.javacpp.opencv_videoio.VideoCapture;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.bytedeco.javacv.OpenCVFrameConverter;
import org.eclipse.swt.graphics.Rectangle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class JavacvService {
	public static final int FACE_WIDTH=50;
	public static final int FACE_HEIGHT=50;
	
	private static final String DEFAULT_CLASSIFER_NAME = "data/haarcascade_frontalface_alt2.xml";
	public static final String FACE_SAVE_DIR = System.getProperty("user.dir")+"\\image\\javacv\\face\\";

	private final Logger log = LoggerFactory.getLogger(getClass());
	
	private CvHaarClassifierCascade classifier = null;
    private CvMemStorage storage = null;
    private CvSeq faces = null;
    private IplImage grabbedImage = null, grayImage = null, smallImage = null;
	
	private AtomicBoolean isStart=new AtomicBoolean(false);
	private static FrameGrabber grabber;
	
	OpenCVFrameConverter.ToIplImage toIplImage=new OpenCVFrameConverter.ToIplImage();
	Java2DFrameConverter java2dFrameConverter=new Java2DFrameConverter();
	private String classiferName=DEFAULT_CLASSIFER_NAME;

	private Point imageSize;
	private BufferedImage faceImage=null;
	private BufferedImage factImage=null;
	
	private List<FaceListener> listFaceListener=new ArrayList<JavacvService.FaceListener>();
	private List<BeforePainListener> listBeforePainListener=new ArrayList<JavacvService.BeforePainListener>();
	
	private boolean isAdapt =true;
	private boolean isFace=true;
	private java.awt.Rectangle rectangle;
	
	public void start() {
		new Thread(new Runnable() {
			public void run() {
				try {
					if (grabber != null) {
						return;
					}
					if (classiferName != null&&isFace) {
						URL url = getClass().getClassLoader().getResource(classiferName);
						File file = new File(url.getFile());
						classifier = new CvHaarClassifierCascade(opencv_core.cvLoad(file.getAbsolutePath()));
						storage = CvMemStorage.create();
					}
					long currentTimeMillis = System.currentTimeMillis();
					grabber = FrameGrabber.createDefault(0);
					grabber.start();
					System.out.println("摄像头启动花费时间："+(System.currentTimeMillis()-currentTimeMillis));
					grabbedImage = toIplImage.convert(grabber.grab());
					grayImage = IplImage.create(grabbedImage.width(), grabbedImage.height(), opencv_core.IPL_DEPTH_8U, 1);
					smallImage = IplImage.create(grabbedImage.width() / 4, grabbedImage.height() / 4, opencv_core.IPL_DEPTH_8U, 1);
					imageSize = new Point(grabbedImage.width(), grabbedImage.height());
				} catch (Exception e) {
					e.printStackTrace();
				}finally{
					isStart.set(true);
				}
			}
		}).start();
	}
	
	public boolean play(Component view){
		System.out.println("isStart==="+isStart.get());
		if (!isStart.get()) {
			return false;
		}
		rectangle = view.getBounds();
		new Thread(new Runnable() {
			public void run() {
				try {
					log.info("准备播放视频");
					while (isStart.get()) {
						grabbedImage=toIplImage.convert(grabber.grab());
						if (grabbedImage==null) {
							continue;
						}
						if (classiferName!=null&&isFace) {
							opencv_core.cvClearMemStorage(storage);
							opencv_imgproc.cvCvtColor(grabbedImage, grayImage, opencv_imgproc.CV_BGR2GRAY);
							opencv_imgproc.cvResize(grayImage, smallImage, opencv_imgproc.CV_INTER_AREA);
							faces = opencv_objdetect.cvHaarDetectObjects(smallImage, classifier, storage, 1.1, 3,
									opencv_objdetect.CV_HAAR_FIND_BIGGEST_OBJECT | opencv_objdetect.CV_HAAR_DO_ROUGH_SEARCH);
						}
						paint(view);
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}).start();
		return true;
	}
	
	public void pause(boolean start){
		isStart.set(start);
	}
	
	private void paint(Component view) {
		Graphics g = view.getGraphics();
		
		if (g==null) {
			return;
		}
		BufferedImage image = java2dFrameConverter.convert(toIplImage.convert(grabbedImage));
		if (classiferName!=null&&faces != null) {
			Graphics2D g2 = image.createGraphics();
				g2.setColor(Color.RED);
				g2.setStroke(new BasicStroke(2));
				int total = faces.total();
				for (int i = 0; i < total; i++) {
					CvRect r = new CvRect(opencv_core.cvGetSeqElem(faces, i));
					int x = r.x() * 4;
					int y = r.y() * 4;
					int width = r.width() * 4;
					int height = r.height() * 4;
					g2.drawRect(x, y, width, height);
					
					faceImage = new BufferedImage(FACE_WIDTH, FACE_HEIGHT, BufferedImage.TYPE_INT_RGB);
					faceImage.getGraphics().drawImage(java2dFrameConverter.convert(toIplImage.convert(grayImage)), 0, 0, FACE_WIDTH, FACE_HEIGHT, x, y, x + width, y + height, null);
					for (FaceListener faceListener : listFaceListener) {
						faceListener.call(g2,faceImage,new Rectangle(x, y, x+width, y+height));
					}
					r.close();
				}
				faces = null;
		}
		for (BeforePainListener beforePainListener : listBeforePainListener) {
			beforePainListener.beforePain(image);
		}
		if (isAdapt) {
			g.drawImage(image, 0, 0,rectangle.width,rectangle.height, view);
		}else
		g.drawImage(image, 0, 0, view);
	}
	public Point getImageSize(){
		return imageSize;
	}
	
	

	public void setClassiferName(String classiferName){
		this.classiferName = classiferName;
	}
	
	public void stop(){
		if (grabber!=null) {
			try {
				grabber.release();
			} catch (Exception e) {
				e.printStackTrace();
			}
			grabber=null;
		}
		isStart.set(false);
	}

	public BufferedImage getFaceImage() {
		return faceImage;
	}
	public BufferedImage getFactImage() {
		try {
			factImage=java2dFrameConverter.convert(grabber.grab());
		} catch (Exception e) {
			e.printStackTrace();
		}
		return factImage;
	}
	
	public void registerFace(String name,BufferedImage... images){
		if (images==null||images.length<1) {
			return;
		}
		try {
//			IplImage cvCreateImage = null;
//			IplImage faceIplImage;
			for (int i = 0; i < images.length; i++) {
				BufferedImage bufferedImage = images[i];
				saveImage(bufferedImage,FACE_SAVE_DIR+name+i+".jpg" );
				
//				faceIplImage = toIplImage.convert(java2dFrameConverter.convert(bufferedImage));
//				cvCreateImage = opencv_core.cvCreateImage(opencv_core.cvGetSize(faceIplImage), 8, 1);
//				opencv_imgproc.cvEqualizeHist(faceIplImage, cvCreateImage);
//				opencv_core.cvSave(FACE_SAVE_DIR+name+i+".jpg", cvCreateImage);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static boolean saveImage(BufferedImage image, String path) throws IOException {
			File file = new File(path);
			if (!file.exists()) {
				file.createNewFile();
			}
			File parent = file.getParentFile();
			if (parent!=null) {
				parent.mkdirs();
			}
			ImageIO.write(image, "jpg", file);
			return true;
	}
	public void getVideoCaptures(){
		
	}
	
	public static void setImage(Component view,BufferedImage image){
		Graphics g = view.getGraphics();
		if (g==null) {
			return;
		}
		g.drawImage(image, 0, 0, view.getWidth(), view.getHeight(), view);
	}
	
	public interface FaceListener{
		public void call(Graphics2D g, BufferedImage faceImage, Rectangle rectangle);
	}
	public interface BeforePainListener{
		public void beforePain(BufferedImage image);
	}
	public void addBeforePainListener(BeforePainListener faceListener){
		this.listBeforePainListener.add(faceListener);
	}
	public void removeBeforePainListener(BeforePainListener faceListener){
		this.listBeforePainListener.remove(faceListener);
	}
	public void clearBeforePainListener(){
		this.listBeforePainListener.clear();
	}
	public void addFaceListener(FaceListener faceListener){
		this.listFaceListener.add(faceListener);
	}
	public void removeFaceListener(FaceListener faceListener){
		this.listFaceListener.remove(faceListener);
	}
	public void clearFaceListener(){
		this.listFaceListener.clear();
	}

	public boolean isAdapt() {
		return isAdapt;
	}

	public void setAdapt(boolean isAdapt) {
		this.isAdapt = isAdapt;
	}

	public boolean isFace() {
		return isFace;
	}

	public void setFace(boolean isFace) {
		this.isFace = isFace;
	}
}
